-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
batching in MultiDiracDeterminant::mw_accept_rejectMove #5071
Conversation
There are three different cases for data members of
In this PR, I currently have it implemented so that anything that is in the MW resource or handled by a dual-space allocator is updated on the device only. Anything that is not dual-space allocated is only updated on the host. Currently, there is no separation based on anything like If the platform is added as a template parameter, at least the @ye-luo @anbenali if you know what data is supposed to be up-to-date upon entering and exiting this function, I'd appreciate any feedback (if not, then I'll start looking at what happens upstream and downstream from here and check with you to see if it makes sense) I can also add any of the data in the list above to the resource collection if it makes sense to do that, but that could also be a separate PR. |
Test this please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preliminary review.
Consider factoring some of this out into more manageable private functions.
for (int iw = 0; iw < wfc_list.size(); iw++) | ||
const int nw = wfc_list.size(); | ||
// separate accepted/rejected walker indices | ||
const int n_accepted = std::count(isAccepted.begin(), isAccepted.begin() + nw, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please have this follow the common idiom of
std::count(isAccepted.begin(), isAccepted.end(), true)
This exceptional looking code lead me and likely other carteful readers to wonder just why isAccepted
could be longer than wfc_list. Also you are tossing the implicit bounds checking that using the begin() end() iterator pair provides.
mw functions "multi walker" arguments must always be the same size you can and should write the code as if there were true. If you have doubts during development use an assert
definitely don't write code that "defends" against this defect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this makes sense; sometimes I try too hard to replicate exactly what the prior logic was even when (like here) that results in some ugly, non-standard code
// setup device pointer lists | ||
switch (wfc_leader.UpdateMode) | ||
{ | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because there are no real unit tests for this class and just a massive procedure test for multislater determinant. I'm not sure whether this coverage warning means none of this default case is covered. My reading is the default case is not covered. I think the ORB_PYPB _PARTIAL/RATIOS cases are covered. Seems like a bunch of important code with no coverage.
Before doing a big refactor why not write the missing unit tests. What's the expected outcome of calling this function?
It seems like this function basically just copies a bunch of state data in response to the isAccepted vector and the internal WorkingIndex state. Significantly there are three modes of this.
Fill members with test data, intialize WorkingIndex, call, check side_effects are correct. You will probably need a testing friend class to break the encapsulation. See src/Estimators/OneBodyDensityMatricesTests.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before doing a big refactor why not write the missing unit tests. What's the expected outcome of calling this function?
My plan going into this refactor was that it wasn't going to be a huge change, and that I was just planning to group some similar work together (the rejects and accepts) without any other change in behavior. I don't yet have a complete picture of everything else upstream and downstream from this function and what different cases might need to be handled.
It seems like this function basically just copies a bunch of state data in response to the isAccepted vector and the internal WorkingIndex state.
This is also my take on what's happening, but the part I'm not sure about (and what I asked about in my comment above) is which state data I can assume to be up-to-date on the host/device before entering this function, and what needs to be up-to-date (and where) after exiting.
This is the only point where I'd planned to possibly deviate from the current functionality (e.g. in the ORB_PBYP_RATIO case for accepted moves, we do the relevant copying (host to host) to psiMinv, psiM, TpsiM, and ratios_to_ref_, and then we do an H2D update of all of those in addition to dpsiM. If there are cases where all of these (and psiV, psiMinv_temp, and new_ratios_to_ref_) are already up-to-date on the device, and if anything downstream from here will only use those on the device, then it would make sense to just do the copy on the device).
} | ||
|
||
// pointers to data for only accepted walkers | ||
OffloadVector<ValueType*> psiMinv_temp_acc_deviceptr_list; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there aren't retained I'm not sure why the copy and pointer list building cases aren't fused and these are just in those cases. Put blocks in the cases and these lists will be constructed only if you are actually using them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this got a bit messy; I got stuck between not wanting to duplicate code but also not wanting to make the fallthrough logic of the switches hard to follow, so I retained the original logic for the actual copying and used different logic for the pointer lists to avoid duplication (and to only resize the ones I'm actually using), and I ended up with something worse than if I'd just committed to one or the other
@kgasperich Are you clear on the next steps? |
@prckent yes, I talked to Ye a few days ago and I think the next steps are mostly clear. I'm going to resolve some of the data movement issues by more closely mirroring the behavior of the existing accept function, and then performance improvements on top of that (e.g. not doing unnecessary H2D transfers in cases where we can just do a device copy) will be added in a subsequent PR. |
I've updated this so that it has the same data movement patterns as the old code, but there are some differences in how it's done compared to the old code.
todo in subsequent PR:
There is a clear need for more refactoring and optimization of this and related functions, and that is why some things are structured the way that they are. I've made some choices (and left in some comments) that I think will make some of the next steps clearer/easier, but if anyone is opposed to that I can tidy it up more. |
Test this please |
CI passes. @kgasperich could you update the PR description to reflect what the current code does? |
I edited the description to summarize what it does and add some more info that might be useful in future refactoring |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Kevin. I think this is more than enough of a step forward for this PR! For posterity it would be helpful to note any before/after timings that you have for this.
Test this please |
Proposed changes
Restructure
MultiDiracDeterminant::mw_accept_rejectMove
to handle all accepted moves together and all rejected moves together (minimize branching).A first pass through all walker moves is done to get a vector of accepted walker indices and a vector of rejected walker indices, then the necessary data movement is done first for all accepted moves and then for all rejected moves.
The code introduced in this PR retains the same data movement patterns as the old code (which is still present in acceptMove and , but there are some differences in how it's done compared to the old code.
std::copy
and do H2D transferBLAS::copy
using pointer lists (can be trivially replaced with copy_batched in subsequent PR when data movement requirements are figured out)todo in subsequent PR:
There is a clear need for more refactoring and optimization of this and related functions, and that is why some things are structured the way that they are. I've made some choices (and left in some comments) that I think will make some of the next steps clearer/easier, but if anyone is opposed to that I can tidy it up more.
current code behavior
here's a table that summarizes the current behavior of
acceptMove
:UpdateMode
the relevant copy occurs (*
denotes copies that only occur when using spinors)UpdateMode
, so even whenUpdateMode==ORB_PBYP_RATIO
, anupdateTo
is called fordpsiM
, even thoughdpsiM
is not modified by this function for that value ofUpdateMode
restore
is much simpler:What type(s) of changes does this code introduce?
Does this introduce a breaking change?
What systems has this change been tested on?
Checklist